/********************************************************************/
/*          demo_main.c                                             */
/*            multi_sio (Multi-play Communication Sample) Main      */
/*                                                                  */
/*          Copyright (C) 1999-2001 NINTENDO Co.,Ltd.               */
/********************************************************************/
//#define CODE32
#include "MultiSio.h"
#include "types.h"
#include "data.h"

//#define KEY_ON                              // Flag that enables the key control OBJ

#define INITIAL_CODE    (*(vu32 *)"AGBJ")   // Initial code


/*-------------------- Global Variables ----------------------------*/

    u16      Cont, Trg;                         // Key input

    u32      IntrMainBuf[0x100];                // Interrupt main routine buffer
 
    u16      BgBak[32*32];                      // BG backup
    OamData  OamBak[128];                       // OAM backup

    u16      HPos[MULTI_SIO_PLAYERS_MAX][2];    // OBJ position data
    u16      VPos[MULTI_SIO_PLAYERS_MAX][2];

    u16      UsrSendBuf[MULTI_SIO_BLOCK_SIZE/2];    // User send buffer
    u16      UsrRecvBuf[MULTI_SIO_PLAYERS_MAX][MULTI_SIO_BLOCK_SIZE/2];
                                                    // User receive buffer

    u32      SioFlags;                              // Communication status
    u32      SioFlagsBak;
    u8       SioStartFlag;                          // Communication start flag

/*---------------------- Subroutine -----------------------------*/

void KeyRead(void);
void BgScSet(u8 *Srcp, u16 *Destp, u8 PlttNo);


/*------------------------------------------------------------------*/
/*                      Interrupt Table                             */
/*------------------------------------------------------------------*/
void IntrDummy(void);
void VBlankIntr(void);

const IntrFuncp IntrTable[13] = {
#ifdef MULTI_SIO_DI_FUNC_FAST
    (IntrFuncp )IntrFuncBuf,    // Serial communication interrupt
#else                           // or Multi-play communication timer interrupt
    MultiSioIntr,
#endif
    VBlankIntr,                 // V-blank interrupt

    IntrDummy,                  // H-blank interrupt
    IntrDummy,                  // V-counter match interrupt
    IntrDummy,                  // DMA0 interrupt
    IntrDummy,                  // DMA1 interrupt
    IntrDummy,                  // DMA2 interrupt
    IntrDummy,                  // DMA3 interrupt
    IntrDummy,                  // Key interrupt
};

IntrFuncp IntrTableBuf[14];


/*==================================================================*/
/*                    Main Routine                                  */
/*==================================================================*/
extern void intr_main(void);

void DemoMain(void)
{
    char  *StrTmp;
    s32    i, ii;

    RegisterRamReset(RESET_CPU_WRAM_FLAG                   // Initialization
                   | RESET_ALL_REG_FLAG ^ RESET_REG_SIO_FLAG);

    DmaCopy(3, IntrTable, IntrTableBuf,sizeof(IntrTableBuf),32);// Sets interrupt table
    DmaCopy(3, intr_main, IntrMainBuf, sizeof(IntrMainBuf), 32);// Sets interrupt main routine
    *(vu32 *)INTR_VECTOR_BUF = (vu32 )IntrMainBuf;

    DmaArrayCopy(3, CharData_Sample, BG_VRAM+0x8000, 32);  // Sets BG character
    DmaArrayCopy(3, CharData_Sample, OBJ_MODE0_VRAM, 32);  // Sets OBJ character
    DmaArrayCopy(3, PlttData_Sample, BG_PLTT,        32);  // Sets BG palette
    DmaArrayCopy(3, PlttData_Sample, OBJ_PLTT,       32);  // Sets OBJ palette

    DmaArrayCopy(3, BgScData_Sample, BgBak,          32);  // Sets BG screen
    DmaArrayCopy(3, BgBak,           BG_VRAM,        32);

    DmaArrayClear(3,160,             OamBak,         32);  // Moves OBJ that is not displayed to outside of the screen
    DmaClear(     3,160,             OAM, OAM_SIZE,  32);
    DmaArrayCopy(3, OamData_Sample,  OamBak,         32);  // Sets OAM
//    DmaArrayCopy(3, OamBak,          OAM,            32);

    for (i=0; i<MULTI_SIO_PLAYERS_MAX; i++)     OamBak[i].VPos = 200;

    *(vu16 *)REG_BG0CNT =                                  // Sets BG control
            BG_COLOR_16 | BG_SCREEN_SIZE_0 | BG_PRIORITY_0
            | 0 << BG_SCREEN_BASE_SHIFT | 2 << BG_CHAR_BASE_SHIFT ;

    *(vu16 *)REG_IE    = V_BLANK_INTR_FLAG;                // Enable V-blank interrupt
    if (*(vu8  *)(ROM_BANK0+0xb2) == 0x96                  // Checks Game Pak is inserted
     && *(vu32 *)(ROM_BANK0+0xac) == INITIAL_CODE)
        *(vu16 *)REG_IE |= CASSETTE_INTR_FLAG;             // Enables Game Pak interrupt
    *(vu16 *)REG_STAT  = STAT_V_BLANK_IF_ENABLE;
    *(vu16 *)REG_IME   = 1;                                // Sets IME

    *(vu16 *)REG_DISPCNT = DISP_MODE_0 | DISP_BG0_ON | DISP_OBJ_ON; // LCDC ON

    MultiSioInit();                                        // Initializes multi-play communication
    UsrSendBuf[0] = 200;
    UsrSendBuf[2] = 200;
    for (i=0; i<MULTI_SIO_PLAYERS_MAX; i++)
        for (ii=0; ii<2; ii++)
            VPos[i][ii] = 200;

    while(1) {
        VBlankIntrWait();                           // Waits for V-blank interrupt

		SioFlagsBak = SioFlags;
        SioFlags = MultiSioMain(UsrSendBuf, UsrRecvBuf);// Multi-play communication main
        if (Trg)   MultiSioStart();                     // Start Multi-play communication

        KeyRead();                                      // Key operation

        if (SioFlags & MULTI_SIO_PARENT) {
            if (!SioStartFlag)    StrTmp = "PUSH ANY KEY  ";
            else                  StrTmp = "              ";
            BgScSet(StrTmp, &((u16 *)BG_VRAM)[32*11+7], 1);
        }

        if (!SioStartFlag) {                            // Initializes OBJ position
            if (SioFlags & 0xf) {                       // after checks connection
                UsrSendBuf[0] = ((OamData *)OamData_Sample)[((SioMultiCnt *)REG_SIOCNT)->ID].VPos;
                UsrSendBuf[1] = ((OamData *)OamData_Sample)[((SioMultiCnt *)REG_SIOCNT)->ID].HPos;
                UsrSendBuf[2] = ((OamData *)OamData_Sample)[((SioMultiCnt *)REG_SIOCNT)->ID].VPos;
                UsrSendBuf[3] = ((OamData *)OamData_Sample)[((SioMultiCnt *)REG_SIOCNT)->ID].HPos;
                SioStartFlag = 1;
            }
        } else {
            for (i=0; i<MULTI_SIO_PLAYERS_MAX; i++) {
                if (SioFlags & (1 << i)) {
                    VPos[i][0] = UsrRecvBuf[i][0];
                    HPos[i][0] = UsrRecvBuf[i][1];
                    VPos[i][1] = UsrRecvBuf[i][2];
                    HPos[i][1] = UsrRecvBuf[i][3];
				} else {
                    VPos[i][0] = VPos[i][1];
                    HPos[i][0] = HPos[i][1];
				}
                OamBak[i].VPos = VPos[i][0];
                OamBak[i].HPos = HPos[i][0];
            }


            // DMA not used in order to enable SIO interrupt
            CpuFastArrayCopy(BgBak,  BG_VRAM);          // Sets BG screen
            CpuFastArrayCopy(OamBak, OAM    );          // Sets OAM


            if ((SioFlags | SioFlagsBak) & 0xe
              && SioFlags & (1 << ((SioMultiCnt *)REG_SIOCNT)->ID)) {
                UsrSendBuf[0] = UsrSendBuf[2];
                UsrSendBuf[1] = UsrSendBuf[3];
#ifndef KEY_ON
                UsrSendBuf[3] -= 4;
#else
                if (Cont & U_KEY)    UsrSendBuf[2] -= 4;
                if (Cont & D_KEY)    UsrSendBuf[2] += 4;
                if (Cont & L_KEY)    UsrSendBuf[3] -= 4;
                if (Cont & R_KEY)    UsrSendBuf[3] += 4;
#endif
            }
        }
    }
}


/*==================================================================*/
/*       Interrupt Routine (Makes process as easy as possible)      */
/*==================================================================*/

/*------------------------------------------------------------------*/
/*                 V-blank Process                                  */
/*------------------------------------------------------------------*/

void VBlankIntr(void)
{
}

/*------------------------------------------------------------------*/
/*              Interrupt Dummy Routine                             */
/*------------------------------------------------------------------*/

void IntrDummy(void)
{
}

/*==================================================================*/
/*                  Subroutine                                      */
/*==================================================================*/

/*------------------------------------------------------------------*/
/*                  Read Key                                        */
/*------------------------------------------------------------------*/

void KeyRead(void)
{
    u16 ReadData = (*(vu16 *)REG_KEYINPUT ^ 0x03ff);
    Trg  = ReadData & (ReadData ^ Cont);            // Trigger input
    Cont = ReadData;                                // Hold input
}

/*------------------------------------------------------------------*/
/*     		Sets BG Screen                                      */
/*------------------------------------------------------------------*/

void BgScSet(u8 *Srcp, u16 *Destp, u8 PlttNo)
{
	while (*Srcp != '\0')
		*Destp++ = (u16 )PlttNo << BG_SC_PLTT_SHIFT | *Srcp++;
}


